﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using HalconDotNet;
using Go;

namespace fhs
{
    abstract class CameraBase
    {
        public enum Source
        {
            Line1,
            Line2,
        }

        public readonly work_engine optionWork;
        VisionName.Camera _name;
        CameraMgr.Type _type;
        string _forceIP;
        string _deviceName;
        HTuple hv_AcqHandle;
        HalconException _lastEc;
        HTuple _reflect = null;
        HTuple _rotate = null;
        bool _immediately = true;

        public CameraBase(VisionName.Camera name,
            CameraMgr.Type type,
            string forceIP,
            string deviceName)
        {
            _name = name;
            _type = type;
            _forceIP = forceIP;
            _deviceName = deviceName;
            hv_AcqHandle = null;
            optionWork = new work_engine();
            optionWork.run(1, ThreadPriority.Highest, true, name.ToString());
            Reset();
        }

        ~CameraBase()
        {
            if (null != hv_AcqHandle)
            {
                HOperatorSet.CloseFramegrabber(hv_AcqHandle);
            }
            Task.Run(() => optionWork.force_stop());
        }

        public VisionName.Camera Name
        {
            get
            {
                return _name;
            }
        }

        public bool Opened
        {
            get
            {
                return null != hv_AcqHandle;
            }
        }

        public void Reset()
        {
            lock (this)
            {
                try
                {
                    _lastEc = null;
                    if (null != hv_AcqHandle)
                    {
                        HOperatorSet.CloseFramegrabber(hv_AcqHandle);
                        hv_AcqHandle = null;
                    }
                    if (null == _forceIP || 0 == _forceIP.Length)
                    {
                        HOperatorSet.OpenFramegrabber(_type.ToString(), 0, 0, 0, 0, 0, 0, "default", -1, "default",
                            -1, "false", "default", _deviceName, 0, -1, out hv_AcqHandle);
                    }
                    else
                    {
                        int count = 0;
                        while (true)
                        {
                            count++;
                            try
                            {
                                try
                                {
                                    HOperatorSet.OpenFramegrabber(_type.ToString(), 0, 0, 0, 0, 0, 0, "default", -1, "default",
                                        -1, "false", "default", _deviceName, 0, -1, out hv_AcqHandle);
                                }
                                catch (HalconException)
                                {
                                    switch (_type)
                                    {
                                        case CameraMgr.Type.GigEVision:
                                            HOperatorSet.OpenFramegrabber(_type.ToString(), 0, 0, 0, 0, 0, 0, "default", -1, "default",
                                                $"GtlForceIP={_forceIP}", "false", "default", _deviceName, 0, -1, out hv_AcqHandle);
                                            break;
                                        case CameraMgr.Type.GigEVision2:
                                            HOperatorSet.OpenFramegrabber(_type.ToString(), 0, 0, 0, 0, 0, 0, "default", -1, "default",
                                                $"force_ip={_forceIP}", "false", "default", _deviceName, 0, -1, out hv_AcqHandle);
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                switch (_type)
                                {
                                    case CameraMgr.Type.GigEVision:
                                        SetParam("GtlGVSPDiscardIncompleteBuffers", "enable");
                                        break;
                                    case CameraMgr.Type.GigEVision2:
                                        break;
                                    default:
                                        break;
                                }
                                break;
                            }
                            catch (HalconException)
                            {
                                if (3 == count)
                                {
                                    throw;
                                }
                            }
                        }
                    }
                }
                catch (HalconException ec)
                {
                    LogMgr.File(ec.Message);
                    LogMgr.Info($"{_name} 相机打开错误");
                    _lastEc = ec;
                    hv_AcqHandle = null;
                }
            }
        }

        public bool OpenOK()
        {
            return null != hv_AcqHandle;
        }

        public virtual void AsyncStart()
        {
            try
            {
                HOperatorSet.GrabImageStart(hv_AcqHandle, -1);
            }
            catch (Exception)
            {
                LogMgr.Error($"{Name} 相机启动异步采集异常");
            }
        }

        public void DiscardIncomplete(bool enable)
        {
            if (CameraMgr.Type.GigEVision == _type)
            {
                SetParam("GtlGVSPDiscardIncompleteBuffers", enable ? "enable" : "disable");
            }
        }

        public virtual void ColorMode()
        {
            SetParam("color_space", "rgb");
        }

        public virtual void GrayMode()
        {
            SetParam("color_space", "gray");
        }

        public virtual void TriggerMode(bool enable)
        {
            if (enable)
            {
                SetParam("TriggerMode", "On");
            }
            else
            {
                SetParam("TriggerMode", "Off");
            }
        }

        public void TriggerSource(Source source)
        {
            SetParam("TriggerSource", source.ToString());
        }

        public virtual void GrabTimeout(int delay)
        {
            SetParam("grab_timeout", delay);
        }

        public virtual void SetExposureTime(int time)
        {
            SetParam("ExposureTime", time);
        }

        public virtual void SetWidth(int width)
        {
            SetParam("Width", width);
        }

        public virtual void SetHeight(int height)
        {
            SetParam("Height", height);
        }

        public void SetParam(HTuple param, HTuple value)
        {
            lock(this)
            {
                try
                {
                    _lastEc = null;
                    HOperatorSet.SetFramegrabberParam(hv_AcqHandle, param, value);
                }
                catch (HalconException ec)
                {
                    _lastEc = ec;
                    LogMgr.Error($"{Name} 相机设置参数异常，Code:{ec.GetErrorCode()}，{param}:{value}");
                }
            }
        }

        public bool Error
        {
            get
            {
                return null != _lastEc;
            }
        }

        public HObject Snap()
        {
            HObject ho_Image;
            lock (this)
            {
                try
                {
                    _lastEc = null;
                    HOperatorSet.GrabImage(out ho_Image, hv_AcqHandle);
                }
                catch (HalconException ec)
                {
                    _lastEc = ec;
                    LogMgr.Error($"{Name} 相机拍照异常，Code:{ec.GetErrorCode()}");
                    return null;
                }
            }
            if (_immediately)
            {
                bool reflect = null != _reflect && 1 == _reflect.Length;
                bool rotate = null != _rotate && 1 == _rotate.Length && 0 != _rotate.D;
                if (reflect || rotate)
                {
                    ho_Image = ImageRotate(ho_Image, rotate ? _rotate : new HTuple(0), reflect ? _reflect : null);
                }
            }
            return ho_Image;
        }

        public HObject SnapAsync()
        {
            HObject ho_Image;
            lock (this)
            {
                try
                {
                    _lastEc = null;
                    HOperatorSet.GrabImageAsync(out ho_Image, hv_AcqHandle, -1);
                }
                catch (HalconException ec)
                {
                    _lastEc = ec;
                    LogMgr.Error($"{Name} 相机异步拍照异常，Code:{ec.GetErrorCode()}");
                    return null;
                }
            }
            if (_immediately)
            {
                bool reflect = null != _reflect && 1 == _reflect.Length;
                bool rotate = null != _rotate && 1 == _rotate.Length && 0 != _rotate.D;
                if (reflect || rotate)
                {
                    ho_Image = ImageRotate(ho_Image, rotate ? _rotate : new HTuple(0), reflect ? _reflect : null);
                }
            }
            return ho_Image;
        }

        public HalconException LastException
        {
            get
            {
                return _lastEc;
            }
        }

        public HTuple Rotate
        {
            set
            {
                _rotate = value.TupleReal();
            }
            get
            {
                return _rotate;
            }
        }

        public HTuple Reflect
        {
            set
            {
                _reflect = value.TupleReal();
            }
            get
            {
                return _reflect;
            }
        }

        public bool Immediately
        {
            set
            {
                _immediately = value;
            }
            get
            {
                return _immediately;
            }
        }

        static public HObject ImageRotate(HObject ho_Image, HTuple hv_InRotate, HTuple hv_InReflect)
        {
            HObject ho_OutImage;
            if (null != hv_InReflect && 0 != hv_InReflect.Length)
            {
                if (0 == hv_InReflect.D % 90 && 0 == hv_InRotate.D % 90)
                {
                    HObject mirror;
                    if (0 == hv_InReflect.D % 180)
                    {
                        HOperatorSet.MirrorImage(ho_Image, out mirror, "row");
                    }
                    else
                    {
                        HOperatorSet.MirrorImage(ho_Image, out mirror, "column");
                    }
                    if (0 != hv_InRotate.D)
                    {
                        HOperatorSet.RotateImage(mirror, out ho_OutImage, hv_InRotate, "constant");
                    }
                    else
                    {
                        ho_OutImage = mirror;
                    }
                    return ho_OutImage;
                }
            }
            else
            {
                if (0 == hv_InRotate.D % 90)
                {
                    if (0 != hv_InRotate.D)
                    {
                        HOperatorSet.RotateImage(ho_Image, out ho_OutImage, hv_InRotate, "constant");
                    }
                    else
                    {
                        ho_OutImage = ho_Image;
                    }
                    return ho_OutImage;
                }
            }
            HTuple hv_Width, hv_Height;
            HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
            hv_Width = hv_Width.TupleReal();
            hv_Height = hv_Height.TupleReal();
            HTuple hv_HomMat2DReflect, hv_HomMat2DRotate;
            if (null != hv_InReflect && 0 != hv_InReflect.Length)
            {
                HTuple hv_HomMat2DIdentity;
                HOperatorSet.HomMat2dIdentity(out hv_HomMat2DIdentity);
                HOperatorSet.HomMat2dReflect(hv_HomMat2DIdentity, 0.5 * hv_Height, 0.5 * hv_Width,
                    (0.5 * hv_Height) + (((hv_InReflect.TupleRad())).TupleSin()), (0.5 * hv_Width) + (((hv_InReflect.TupleRad()
                    )).TupleCos()), out hv_HomMat2DReflect);
            }
            else
            {
                HOperatorSet.HomMat2dIdentity(out hv_HomMat2DReflect);
            }

            HTuple hv_RotateRow1 = null, hv_RotateCol1 = null, hv_RotateRow2 = null, hv_RotateCol2 = null;
            HTuple hv_RotateRow3 = null, hv_RotateCol3 = null, hv_RotateRow4 = null, hv_RotateCol4 = null;
            HOperatorSet.HomMat2dRotate(hv_HomMat2DReflect, hv_InRotate.TupleRad(), 0.5 * hv_Height,
                0.5 * hv_Width, out hv_HomMat2DRotate);
            HOperatorSet.AffineTransPixel(hv_HomMat2DRotate, hv_Height, hv_Width, out hv_RotateRow1,
                out hv_RotateCol1);
            HOperatorSet.AffineTransPixel(hv_HomMat2DRotate, hv_Height, 0, out hv_RotateRow2,
                out hv_RotateCol2);
            HOperatorSet.AffineTransPixel(hv_HomMat2DRotate, 0, 0, out hv_RotateRow3, out hv_RotateCol3);
            HOperatorSet.AffineTransPixel(hv_HomMat2DRotate, 0, hv_Width, out hv_RotateRow4,
                out hv_RotateCol4);
            HTuple hv_HomMat2DRotateTranslate = null;
            HOperatorSet.HomMat2dTranslate(hv_HomMat2DRotate, -(new HTuple(hv_RotateRow1, hv_RotateRow2, hv_RotateRow3, hv_RotateRow4).TupleMin()),
                -(new HTuple(hv_RotateCol1, hv_RotateCol2, hv_RotateCol3, hv_RotateCol4).TupleMin()), out hv_HomMat2DRotateTranslate);
            HOperatorSet.AffineTransImage(ho_Image, out ho_OutImage, hv_HomMat2DRotateTranslate,
                "constant", "true");
            return ho_OutImage;
        }
    }
}
